package ro.upt.dcda.pricezone.s2;

import java.util.Hashtable;
import java.util.Properties;

import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueReceiver;
import javax.jms.QueueSender;
import javax.jms.QueueSession;
import javax.jms.Session;
import javax.jms.StreamMessage;
import javax.naming.Context;
import javax.naming.InitialContext;

import ro.upt.dcda.pricezone.s2.requests.AllStoresRequest;
import ro.upt.dcda.pricezone.s2.requests.NewProductInfoRequest;
import ro.upt.dcda.pricezone.s2.requests.NewStoreRequest;
import ro.upt.dcda.pricezone.s2.requests.ProductInfoRequest;
import ro.upt.dcda.pricezone.s2.requests.S2Request;
import ro.upt.dcda.pricezone.s2.requests.StoresRequest;

import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LinearRing;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.Polygon;
import com.vividsolutions.jts.geom.PrecisionModel;

public class S2 {

	private Context context;
	private QueueSession session;
	private Queue queue;
	private QueueConnection connection;
	private QueueReceiver queueReceiver;
	private QueueSender queueSender;
	private Polygon area;
	private String queueName;
	private String respQueueName;
	private String name;
	public static final GeometryFactory FACTORY = new GeometryFactory(
			new PrecisionModel(PrecisionModel.FLOATING), 4269);

	public S2() throws Exception {
		loadProperties();
		Hashtable<Object, Object> properties = new Hashtable<>();
		properties.put(Context.INITIAL_CONTEXT_FACTORY,
				"org.exolab.jms.jndi.InitialContextFactory");
		properties.put(Context.PROVIDER_URL, "tcp://127.0.0.1:3035/");
		context = new InitialContext(properties);
		System.out.println("S2 connecting to queues");
		QueueConnectionFactory factory = (QueueConnectionFactory) context
				.lookup("JmsQueueConnectionFactory");
		connection = factory.createQueueConnection();
		session = connection
				.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
		queue = (Queue) context.lookup(queueName);

		connection.start();
		queueReceiver = session.createReceiver(queue);
		queueSender = session.createSender((Queue) context
				.lookup(respQueueName));
	}

	public String getName() {
		return name;
	}

	private void loadProperties() throws Exception {
		Properties prop = new Properties();
		prop.load(Thread.currentThread().getContextClassLoader()
				.getResourceAsStream("instance.properties"));
		this.name = prop.getProperty("name");
		double xmin = Double.valueOf(prop.getProperty("xmin"));
		double xmax = Double.valueOf(prop.getProperty("xmax"));
		double ymin = Double.valueOf(prop.getProperty("ymin"));
		double ymax = Double.valueOf(prop.getProperty("ymax"));
		LinearRing ring = FACTORY.createLinearRing(new Coordinate[] {
				new Coordinate(xmin, ymin), new Coordinate(xmin, ymax),
				new Coordinate(xmax, ymax), new Coordinate(xmax, ymin),
				new Coordinate(xmin, ymin) });
		area = FACTORY.createPolygon(ring, null);
		queueName = prop.getProperty("queue");
		respQueueName = prop.getProperty("queueResp");
	}

	public boolean isRangeOutsideServer(Point point, float range) {
		double distance = this.area.distance(point);
		System.out.println("Distance to " + point + " is " + distance
				+ ", range:" + range);
		return distance < range * 10000;
	}

	public void start() {
		try {
			while (true) {
				try {
					receive();
				} catch (Exception e) {
					e.printStackTrace(System.out);
				}
			}
		} finally {
			try {
				connection.close();
			} catch (JMSException e) {
				e.printStackTrace(System.out);
			}
		}
	}

	private void sendFake() throws JMSException {
		for (int i = 0; i < 20; i++) {
			StreamMessage response = session.createStreamMessage();
			response.setJMSCorrelationID("fake");
			response.writeString("fake");
			response.reset();
			queueSender.send(response);
		}
	}

	public void receive() throws Exception {
		Message tmp = queueReceiver.receive();
		if (tmp == null || !(tmp instanceof StreamMessage)
				|| tmp.getJMSCorrelationID().equals("fake")) {
			return;
		}
		StreamMessage message = (StreamMessage) tmp;
		String requestCode = message.readString();
		System.out.println("Request received:" + requestCode + " "
				+ message.getJMSCorrelationID());
		StreamMessage response = session.createStreamMessage();
		response.setJMSCorrelationID(message.getJMSCorrelationID());
		S2Request request;
		switch (requestCode) {
		case "stores":
			request = new StoresRequest(message, this);
			break;
		case "allStores":
			request = new AllStoresRequest(this);
			break;
		case "newStore":
			request = new NewStoreRequest(message);
			break;
		case "prices":
			request = new ProductInfoRequest(message);
			break;
		case "newPrice":
			request = new NewProductInfoRequest(message);
			break;
		default:
			System.out.println("Request " + requestCode + " not handled");
			return;
		}
		request.work(response);
		response.reset();
		queueSender.send(response);
		// sendFake();
		System.out.println("Sent " + response.getJMSCorrelationID()
				+ " response.");
	}
}
